package picobot.simulation;

import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

import picobot.bot.Picobot;
import picobot.interfaces.core.IMap;
import picobot.interfaces.core.IPicobot;
import picobot.interfaces.core.IRule;
import picobot.interfaces.core.ISimulator;

/**
 * a class that represents a simulator
 */
public class Simulator implements ISimulator {

	private IPicobot picobot;
	private IMap map;
	private Set<int[]> traversedCells = new HashSet<int[]>();

	public Simulator() {
		super();
	}

	/**
	 * this method puts the picobot to an empty random cell
	 */
	public void toEmptyRandomCell() {
		boolean bol = true;

		while (bol) {
			int x = new Random().nextInt(map.getWidth());
			int y = new Random().nextInt(map.getHeight());

			if (map.getCellKind(x, y).equals("FREE")) {
				picobot.setInitialPosition(x, y);

				bol = false;
			}
		}
	}

	@Override
	public void loadPicobot(IPicobot bot) {
		if ((bot.getRules() != null) && (!bot.getRules().isEmpty())) {
			this.picobot = bot;

			if (map != null) {

				toEmptyRandomCell();

			}

		}
	}

	@Override
	public void loadMap(IMap map) {
		this.map = map;

		if (picobot != null) {

			toEmptyRandomCell();
		}

	}

	@Override
	public IMap getMap() {
		return map;
	}

	@Override
	public IPicobot getPicobot() {
		return picobot;
	}

	@Override
	public void step() {
		// adding the current position of the picobot to the traversed cells
		int[] entry = new int[2];
		entry[0] = picobot.getXCoordinate();
		entry[1] = picobot.getYCoordinate();

		if (!isAlreadyTraversed(entry)) {
			traversedCells.add(entry);
		}

		// gets the first applicable rule for the loaded picobot with respect to
		// the loaded map,
		// and applys it to the picobot
		picobot.apply(picobot.getApplicableRule(map));

	}

	@Override
	public Set<int[]> getTraversedCells() {
		return traversedCells;
	}

	@Override
	public boolean isMissionCompleted() {
		int height = map.getHeight();
		int width = map.getWidth();

		Set<int[]> freeCells = new HashSet<int[]>();
		for (int y = height - 1; y >= 0; y--) {
			for (int x = 0; x < width; x++) {
				if (map.getCellKind(x, y).equals("FREE")) {
					int[] entry = new int[2];
					entry[0] = x;
					entry[1] = y;
					freeCells.add(entry);
				}
			}
		}

		boolean bol = true;

		for (int[] free : freeCells) {
			if (!isAlreadyTraversed(free)) {
				bol = false;
				break;
			}
		}
		return bol;
	}

	/**
	 * a supporting class for isMissionCompleted(), that checks if a given
	 * integer array is in the set of traversed cells
	 * 
	 * @param entry
	 *            the array to check
	 * @return true if the array is already traversed, false otherwise
	 */
	public boolean isAlreadyTraversed(int[] entry) {
		boolean bol = false;

		for (int[] traversed : getTraversedCells()) {
			if ((entry[0] == traversed[0]) & (entry[1] == traversed[1])) {
				bol = true;
				break;
			}
		}

		return bol;
	}

	@Override
	public void run(int nSteps) {

		if ((map == null) && (picobot == null)) {
			throw new RuntimeException();
		} else {
			for (int i = 0; i < nSteps; i++) {
				step();
			}

		}

	}

	@Override
	public void reset() {
		traversedCells.clear();

		List<IRule> currentRules = picobot.getRules();
		picobot = new Picobot("default");

		picobot.loadRules(currentRules);

		toEmptyRandomCell();

	}

}
